/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */

#ifndef __GAME_OBJECT_H__
#define __GAME_OBJECT_H__

#include "Object.h"
#include "TransformComponent.h"
#include "EntityComponent.h"
#include "ScriptComponent.h"
#include "RigidBodyComponent.h"
#include "ColliderComponent.h"
#include "LightComponent.h"

namespace PQEngine
{
	enum GameObjectType
	{
		//Root
		GAME_OBJECT_ROOT,

		//Movable
		GAME_OBJECT_EMPTY,
		GAME_OBJECT_ENTITY,
		GAME_OBJECT_LIGHT,
		GAME_OBJECT_CAMERA,

		//None movable
		GAME_OBJECT_TERRAIN,
	};

	class GameObject
		: public Object
	{
	public:
		friend class SceneObject;
		friend class SceneResourceLoader;
		typedef std::map<Ogre::SceneNode*,GameObject*> MapList;
	protected:
		ComponentVector _components[COMPONENT_TYPE_COUNT];
		//ComponentVector _components;//id
		GameObjectVector _children;
		GameObject* _parent;
		SceneObject* _sceneObject;//owner of the gameobject

		inline void setObjectType(ObjectType type)
		{
			_objectType=type;
		}

	public:
		static GameObject* get(Ogre::SceneNode* node);
		static GameObject* getRootObject();

		GameObject(SceneObject* sceneObject,GameObjectType type);
		~GameObject();

		inline std::string getName()
		{
			return _name;
		}

		inline void setName(std::string name)
		{
			_name=name;
		}


		int getChildrenCount();
		GameObject* getChild(int index);

		void addChild(GameObject *object);
		GameObject* getParent();
		void setParent(GameObject* parent);
		void removeChild(GameObject *object);

		void setMaterialName(std::string materialName);
		void setNormalMaterial();

		float getRadius(void);

		Ogre::SceneNode* getSceneNode();
		Ogre::Vector3 getBoundingBoxSize(void);
		Ogre::AxisAlignedBox getBoundingBox(void);
		Ogre::AxisAlignedBox getWorldBoundingBox(void);
		
		void setPosition(float x,float y,float z);
		void setPosition(Ogre::Vector3 position);
		void setOrientation(Ogre::Quaternion orient);
		void setScale(Ogre::Vector3 scale);

		int getComponentCount();
		Component* getComponent(int index);
		
		Component* getColliderComponent();
		TransformComponent* getTransformComponent();
		virtual EntityComponent* getEntityComponent();
		ScriptComponent* getScriptComponent(std::string scriptPath);
		RigidBodyComponent* getRigidBodyComponent();
		void addComponent(Component *component);
		bool hasComponent(Component *component);
		bool removeComponent(Component *component);

		/*Select current game object, may do highlight or etc.
		@param selected Whether or not set the current object selected.
		*/
		virtual void selectObject(bool selected);
		
		inline bool isSelected()
		{
			return _selected;
		}

		inline Ogre::Vector3 getPosition()
		{
			if(_sceneNode){
				return _sceneNode->getPosition();
			}
			return Ogre::Vector3::ZERO;
		}

		inline Ogre::Vector3 getDerivedPosition()
		{ 
			if(_sceneNode){
				return _sceneNode->_getDerivedPosition(); 
			}
			return Ogre::Vector3::ZERO;
		}

		inline Ogre::Quaternion getOrientation()
		{
			if(_sceneNode){
				return _sceneNode->getOrientation(); 
			}
			return Ogre::Quaternion::IDENTITY;
		}

		inline Ogre::Vector3 getScale()
		{
			if(_sceneNode){
				return _sceneNode->getScale(); 
			}
			return Ogre::Vector3::ZERO;
		}

		inline const Ogre::Vector3 &getWorldPosition() const 
		{
			if(_sceneNode){
				return _sceneNode->_getDerivedPosition();
			}
			return Ogre::Vector3::ZERO;
		}

		inline const Ogre::Quaternion &getWorldOrientation() const 
		{
			if(_sceneNode){
				return _sceneNode->_getDerivedOrientation();
			}
			return Ogre::Quaternion::IDENTITY;
		}

		inline bool isRigidBody(){return _isRigidBody;}
		inline bool hasCollider(){return _hasCollider;}
		inline bool isTerrain(){return _isTerrain;}
		inline bool isEntity(){return _isEntity;}
		inline bool isRoot(){return _isRoot;}
		inline bool isLight(){return _isLight;}
		inline GameObjectType getGameObjectType(){ return _gameObjectType;}
		void update(float deltaT);

	private:
		void initGameObject(GameObjectType type);
		void initEmptyObject();

		void addTransform(TransformComponent* component);
		void addEntity(EntityComponent* component);
		void addLight(LightComponent* component);
		void addTerrain(Component* component);

		void addCollider(Component* component);
		void addRigidBody(Component* component);
		void addScript(Component* component);
	protected:
		static MapList _list;
		static Ogre::SceneNode*  _rootSceneNode;
		static GameObject* _root;

		std::string _name;//object name
		Ogre::SceneNode* _sceneNode;//represent transform
		GameObjectType _gameObjectType;

		bool _isRoot;
		bool _isRigidBody;
		bool _isTerrain;
		bool _isEntity;
		bool _isLight;
		bool _hasCollider;
		bool _selected;
	};
}
#endif
